#include "rasterScanTwoPassAPI.h" 

void printList(unsigned int listNumber);


/* ------------------------ Public Methods ------------------------ */

void i_initRasterScanTwoPass(char * rasterScanTwoPassFileName)
{
  
  FILE *fptr;        /* file pointer that holds image */

  printf("\nInitiating a RasterScanTwoPass Sequence...\n\n");
  
  /* open image */
  if((fptr = fopen(rasterScanTwoPassFileName, "r")) == NULL)
  {
    /* did not open file */
    printf("ERROR IN OPENING FILE\n");
    return;
  }

  /* save name of image */
  strcpy(rasterScanTwoPassImage.name,rasterScanTwoPassFileName);

  /* get first string of file */
  fscanf(fptr,"%s",buf);

  /* checking for correct file format */
  /* TODO: make this account for all .pnm file types */
  if (buf[1] == '1') 
  {
    /* find the end of this row, preventative */
    fscanf(fptr,"%s",buf);
    while (buf[0] != '\n') fscanf(fptr,"%c",&buf[0]);
  }
  else
  {
    printf("ERROR NOT A .pbn FILE\n");
    return;
  }

  /* Get image size */
  fscanf(fptr,"%d %d\n",&(rasterScanTwoPassImage.ncols),&(rasterScanTwoPassImage.nrows));

  /* allocate memory for the pixel matrix */
  rasterScanTwoPassImage.rawImage = rasterScanTwoPassAllocateMatrix(rasterScanTwoPassImage.nrows,rasterScanTwoPassImage.ncols);

  /* load matrix with individual pixel values */
  for(loopCountX = 0; loopCountX < rasterScanTwoPassImage.nrows; loopCountX++)
  {
    
    for(loopCountY = 0; loopCountY < rasterScanTwoPassImage.ncols; loopCountY++)
    {

      /* get next character, since we are working with ascii .pbn file */
      fscanf(fptr,"%c",buf);

      /* filter out carriage return */
      if(0x0A == *buf) fscanf(fptr,"%c",buf);

      /* load matrix with individual characters */
      rasterScanTwoPassImage.rawImage[loopCountX][loopCountY] = *buf;

    }

  }

  /* close file, we don't need it anymore */
  fclose(fptr);

  /* initialize components */
  linkedListInit();

  labelName = 'a';
  finalLabelName = 'a';

}

void i_printRasterScanTwoPassMatrix(void)
{
  int x, y;

  /* loop through 2d array for printing */
  for(x = 0; x < rasterScanTwoPassImage.nrows; x++)
  {
    for(y = 0; y < rasterScanTwoPassImage.ncols; y++)
    {

      /* sniff out white areas of image */
      if('0' == rasterScanTwoPassImage.rawImage[x][y])
      {

        /* white area is printed with an '-', instead of '0' for
         * better visual viewing on terminal */
        printf(". ");

      }
      else
      {

        /* print label associated with black part of image */
        printf("%c ",rasterScanTwoPassImage.rawImage[x][y]);

      }

    }

    printf("\n");

  }

  printf("\n\n");

}


void i_startAutoRasterScanTwoPass(RASTERSCANTWOPASSDISPLAYTYPE rasterScanTwoPassDisplayType)
{
  
  /* clear recursive call counter */
  totalRasterScanTwoPassRecursiveCall = 0;

  switch (rasterScanTwoPassDisplayType)
  {
    case RASTERSCANTWOPASS_NO_DISPLAY:

      /* to be filled out later */
      break;

    case RASTERSCANTWOPASS_DISPLAY_END:

      printAnimation = 0;

      break;

    case RASTERSCANTWOPASS_DISPLAY_RASTERSCANTWOPASS_ANIMATION:

      printAnimation = 1;

      /* to be filled out later */
      break;

    default:

      /* error trap */
      break;

  }

  rasterScanTwoPassScanOne();

  printf("Starting RasterScanTwoPass Sequence...\n\n");

  printRasterScanTwoPassMatrix();

  checkForNeedToMergeLists();

  rasterScanTwoPassScanTwo();

  printf("\nPrinting Relabeled Matrix...\n\n");

  printRasterScanTwoPassMatrix();

  /* unload all linked list memory here */
  equivalenceListCounter = 0;

  /* scroll through the equivalence list */
  while(NULL != equivalenceList[equivalenceListCounter])
  {
#ifdef DEBUG
    printf("Removing List %d\n", equivalenceListCounter);
#endif
    /* keep removing first entry until all list items have been freed */
    while(!linkedListEmpty(&equivalenceList[equivalenceListCounter]))
    {
      if(!linkedListRemoveFirst(&equivalenceList[equivalenceListCounter],
                                removeListItemFromMemory)) break;
    }
    /* goto to next equivalence linked list */
    equivalenceListCounter++;
  }
#ifdef DEBUG
  printf("\nRemoving Final Tag List\n");
#endif
  /* release final tag linked list from memory */
  while(!linkedListEmpty(&finalTagList))
  {
    if(!linkedListRemoveFirst(&finalTagList, removeFinalTagListItemFromMemory)) break;
  }
}

void i_deallocRasterScanTwoPass(void)
{
  unsigned int interval;

  for(interval = 0; interval < rasterScanTwoPassImage.nrows; interval++)
  {
    free(rasterScanTwoPassImage.rawImage[interval]);
  }
  free(rasterScanTwoPassImage.rawImage);
}

/* ------------------------ Private Methods ------------------------ */

void rasterScanTwoPassScanOne(void)
{
  unsigned int eqListCounter;
  unsigned int foundFlag;
  /* loop through all pixels in the image */
  for(loopCountY = 0; loopCountY < rasterScanTwoPassImage.ncols; loopCountY++)
  {
    for(loopCountX = 0; loopCountX < rasterScanTwoPassImage.nrows; loopCountX++)
    {
      if('0' != rasterScanTwoPassImage.rawImage[loopCountY][loopCountX])
      {
        if(('0' != westPixelValue()) && ('0' != northPixelValue()))
        {
          
          if(westPixelValue() != northPixelValue())
          {
            /* fill list data buffer */
            listDataBuffer.xCoordLink1 = loopCountX;
            listDataBuffer.yCoordLink1 = loopCountY;
            listDataBuffer.labelLink1 = westPixelValue();

            listDataBuffer.xCoordLink2 = loopCountX;
            listDataBuffer.yCoordLink2 = loopCountY - 1;
            listDataBuffer.labelLink2 = northPixelValue();

            /* init for checking if the relationship exists */
            eqListCounter = 0;
            /* go throught list */
            while(NULL != equivalenceList[eqListCounter])
            {
              /* check if west component exist in a list */
              if((-99 != linkedListDoesExistInList(&equivalenceList[eqListCounter], 
                                           &listDataBuffer.labelLink1, 
                                           compareListItemCallBack)) ||
                 (-99 != linkedListDoesExistInList(&equivalenceList[eqListCounter], 
                                           &listDataBuffer.labelLink2, 
                                           compareListItemCallBack)))
              {
                if((-99 == linkedListDoesExistInList(&equivalenceList[eqListCounter], 
                                           &listDataBuffer, 
                                           compareListEquivalenceExist)))
                {
                  linkedListInsert(&equivalenceList[eqListCounter], 
                                   &listDataBuffer, 
                                   sizeof(listDataBuffer)); 
                }
                foundFlag = 1;
                break;
              }
              /* increment counter */
              eqListCounter++;
            }

            if(!foundFlag)
            {
              linkedListInsert(&equivalenceList[equivalenceListCounter++], 
                               &listDataBuffer, 
                               sizeof(listDataBuffer));
            }
            foundFlag = 0;
          }
          /* set pixel to west regardless of previous condition */
          currentPixel = westPixelValue();
        }
        else if(('0' != westPixelValue()) && ('0' == northPixelValue()))
        {
          /* label to west label */
          currentPixel = westPixelValue();
        }
        else if(('0' == westPixelValue()) && ('0' != northPixelValue()))
        {
          /* label to north label */
          currentPixel = northPixelValue();
        }
        else
        {
          /* create new label for current pixel */
          currentPixel = labelName++;
        }
      }
    }
  }
}

void rasterScanTwoPassScanTwo(void)
{
  /* loop through all pixels in the image */
  for(loopCountY = 0; loopCountY < rasterScanTwoPassImage.ncols; loopCountY++)
  {
    for(loopCountX = 0; loopCountX < rasterScanTwoPassImage.nrows; loopCountX++)
    {
      if('0' != rasterScanTwoPassImage.rawImage[loopCountY][loopCountX])
      {
        /* check to see if final label exists */
        finalLabelRoutine();
      }
    }
  }
}

/* TODO: rename appropriately */
/* TODO: fix speghetti code, looks like garbage */
void finalLabelRoutine(void)
{
  unsigned int firstPassListCount = 0;
  unsigned int elementPosition;
  unsigned int firstPassListPosition;
  unsigned int finalListPosition;
  listFinalLabel * finalLabelPtr; 
  listData * listDataPtr = (listData *)malloc(sizeof(listData));


  if(-99 != (finalListPosition = linkedListDoesExistInList(&finalTagList, &currentPixel, compareFinalLabelCallBack)))
  {
    finalLabelPtr = linkedListGetDataFromPosition(&finalTagList, finalListPosition);
    currentPixel = finalLabelPtr->finalLabel;
    return;
  }

  /* scroll through first pass lists to see if there is a match */
  /* if there is a match, then go through that list for a final match in the final list */

  /* go through all lists */
  while(NULL != equivalenceList[firstPassListCount])
  {
    /* check to see if pixel exists in a list */
    if(-99 != linkedListDoesExistInList(&equivalenceList[firstPassListCount], 
                                 &currentPixel, 
                                 compareListItemCallBack))
    {
      elementPosition = 0;
      /* go through each element of this list */
      while((listDataPtr = linkedListGetDataFromPosition(&equivalenceList[firstPassListCount], elementPosition++)) != NULL)
      {
        /* check to see if first label is a match */
        if(-99 != (firstPassListPosition = linkedListDoesExistInList(&finalTagList, &listDataPtr->labelLink1, compareFinalLabelCallBack)))
        {
          finalLabelPtr = linkedListGetDataFromPosition(&finalTagList, firstPassListPosition);
          insertIntoFinalList(currentPixel, finalLabelPtr->finalLabel);
          currentPixel = finalLabelPtr->finalLabel;
          free(listDataPtr); 
          return;
        }

        /* check to see if second label is a match */
        if(-99 != (firstPassListPosition = linkedListDoesExistInList(&finalTagList, &listDataPtr->labelLink2, compareFinalLabelCallBack)))
        {
          finalLabelPtr = linkedListGetDataFromPosition(&finalTagList, firstPassListPosition);
          insertIntoFinalList(currentPixel, finalLabelPtr->finalLabel);
          currentPixel = finalLabelPtr->finalLabel;
          free(listDataPtr); 
          return;
        }
      }
    }
    firstPassListCount++;
  }

  /* all other detections failed, so assigning a new label */
  insertIntoFinalList(currentPixel, finalLabelName);
  currentPixel = finalLabelName++;
  free(listDataPtr); 
  return;
}

void insertIntoFinalList(char currentLabel, char finalLabel)
{
  listFinalLabel * finalLabelBuff = (listFinalLabel *)malloc(sizeof(listFinalLabel));

  finalLabelBuff->firstPassLabel = currentLabel;
  finalLabelBuff->finalLabel = finalLabel;

  if(-99 == linkedListDoesExistInList(&finalTagList, &currentLabel, compareFinalLabelCallBack))
  {
    linkedListInsert(&finalTagList, finalLabelBuff, sizeof(listFinalLabel));
  }

  free(finalLabelBuff);
}

unsigned char westPixelValue(void)
{
  /* boundary guard */
  if(loopCountX)
  {
    return westPixel;
  }
  else
  {
    return '0';
  }
}

unsigned char northPixelValue(void)
{
  /* boundary guard */
  if(loopCountY)
  {
    return northPixel;
  }
  else
  {
    return '0';
  }
}

void removeListItemFromMemory(void * data)
{

#ifdef DEBUG
  listData * tempData = (listData*)data;

  printf("Removing data object location: %2d, %2d, %c, ", tempData->xCoordLink1, 
                                                          tempData->yCoordLink1,
                                                          tempData->labelLink1);
  printf("linked to: %2d, %2d, %c\n", tempData->xCoordLink2, 
                                      tempData->yCoordLink2,
                                      tempData->labelLink2);
#endif

  /* check for memory leak here, possible source */
  free((listData*)data);
}

void removeFinalTagListItemFromMemory(void * data)
{
#ifdef DEBUG
  printf("Removing Final List Tag Object: FirstPass %c, FinalTag %c\n",
          ((listFinalLabel *)data)->firstPassLabel,
          ((listFinalLabel *)data)->finalLabel);
#endif
  free((listFinalLabel *)data);
}

unsigned int compareListItemCallBack(void * dataStruct, void * data)
{
  listData * tempList = dataStruct;

  if(*(char*)data == tempList->labelLink1 ||
     *(char*)data == tempList->labelLink2)
  {
    return 1;
  }
  return 0;
}

unsigned int compareListEquivalenceExist(void * listStruct, void * newStruct)
{
  listData * existingStruct = listStruct;
  listData * currentStruct = newStruct;

  if(((existingStruct->labelLink1 == currentStruct->labelLink1)  &&
      (existingStruct->labelLink2 == currentStruct->labelLink2)) ||
     ((existingStruct->labelLink1 == currentStruct->labelLink2)  &&
      (existingStruct->labelLink2 == currentStruct->labelLink1))) 
  {
    return 1;
  }
  return 0;
}

unsigned int compareFinalLabelCallBack(void * listStruct, void * firstPassLabel)
{
  listFinalLabel * existingLabel = listStruct;

  return (existingLabel->firstPassLabel == *(char *)firstPassLabel);

}

/* TODO: only covers in one direction,
 * cannot cover all shapes correctly??? */
void checkForNeedToMergeLists(void)
{
  listData * listData1;
  unsigned int currentListCount = 0;
  unsigned int currentListCheck = 0;
  unsigned int elementPosition;

  /* go through list elements */
  while(NULL != equivalenceList[currentListCount])
  {
    /* only looking at lists that occur after the current list */
    currentListCheck = currentListCount;
    elementPosition = 0;
    /* scroll through each element of the array */
    while((listData1 = linkedListGetDataFromPosition(&equivalenceList[currentListCheck], elementPosition)) != NULL)
    {
      /* check to see if next list exists */
      while(NULL != equivalenceList[++currentListCheck])
      {
        /* check to see if there is a match in the next list */
        if(checkForExistence(currentListCheck, listData1))
        {
          /* found match, merge lists here through function call */
          mergeLists(currentListCount, currentListCheck);
          break;
        }
      }
      /* no further lists to check for the current element */
      elementPosition++;
      currentListCheck = currentListCount;
    }

    currentListCount++;
  }
}

void mergeLists(unsigned int mergePut, unsigned int mergeGet)
{
  unsigned int elementPosition = 0;
  unsigned int listPositionCount = 0;
  listData * listDataTemp;


  while((listDataTemp = linkedListGetDataFromPosition(&equivalenceList[mergeGet], elementPosition++)) != NULL)
  {
    linkedListInsert(&equivalenceList[mergePut], listDataTemp, sizeof(listData));

    if(elementPosition > 15) break;
  }

  /* delete empty list */
  while(!linkedListEmpty(&equivalenceList[mergeGet]))
  {
    if(!linkedListRemoveFirst(&equivalenceList[mergeGet],
                              removeListItemFromMemory)) break;
  }

  listPositionCount = 0;
  /* find empty position */
  while(NULL != equivalenceList[listPositionCount]) listPositionCount++;

  /* start shifting */
  while(NULL != equivalenceList[listPositionCount + 1])
  {
    equivalenceList[listPositionCount] = equivalenceList[listPositionCount + 1];
    listPositionCount++;
  }
  equivalenceList[listPositionCount] = NULL;
}

unsigned int checkForExistence(unsigned int listToCheck, listData * listDataPtr)
{
  if((-99 != linkedListDoesExistInList(&equivalenceList[listToCheck], 
                               &listDataPtr->labelLink1, 
                               compareListItemCallBack)) ||
     (-99 != linkedListDoesExistInList(&equivalenceList[listToCheck], 
                               &listDataPtr->labelLink2, 
                               compareListItemCallBack)))
  {

    return 1;
  }
  return 0;
}

unsigned char **rasterScanTwoPassAllocateMatrix(int length, int height)
{
   unsigned char **matrix;
   int i;
   
   /* allocate memory for the for the pointers of each row */
   matrix = (unsigned char **)malloc(sizeof(unsigned char *) * length);
   /* populate other pointers with array length */
   for(i = 0; i < length; i++)
   {
     /* allocate memory for the column positions in each row */
     matrix[i]= (unsigned char *) calloc(height, sizeof(unsigned char));
   }
   return(matrix);
}

void printFinalList(void)
{
  unsigned int elementPosition = 0;
  listFinalLabel * finalLabelBuff = (listFinalLabel *)malloc(sizeof(listFinalLabel));

  while((finalLabelBuff = linkedListGetDataFromPosition(&finalTagList, elementPosition++)) != NULL)
  {
    printf("FinalLabelLink->FirstPass: %c, FinalLabelLink->FinalLabel: %c\n",
            finalLabelBuff->firstPassLabel, finalLabelBuff->finalLabel);
  }
  free(finalLabelBuff);
}

void printList(unsigned int listNumber)
{
  listData * listDataPtr;
  unsigned int elementPosition = 0;

  printf("Printing List %d\n", listNumber);
  while((listDataPtr = linkedListGetDataFromPosition(&equivalenceList[listNumber], elementPosition++)) != NULL)
  {
    printf("Label 1: %c, Label 2: %c\n", listDataPtr->labelLink1, listDataPtr->labelLink2);
  }
}
